1. 파일 오픈과 충돌 방지 락 (File Lock)
-
파일을 열었을 때 OS 차원에서 다른 프로세스와의 동시 수정이나 삭제를 차단하기 위해 기본적으로 파일 락(Lock)이 설정됩니다.
-
특히
write모드로 열었을 경우, 여러 프로세스가 동시에 같은 위치에 데이터를 기록하여 파일이 깨지는 현상(충돌)을 방지합니다. -
OS별 특징:
- Windows:
fopen으로 쓰기 모드 오픈 시 OS 레벨에서 강제 락(Mandatory Lock)이 걸려 외부에서의 수정/삭제가 차단됩니다. - Linux/POSIX: 기본적으로 권고 락(Advisory Lock) 정책을 따르므로, 완벽한 프로세스 간 동기화가 필요할 때는
flock()또는fcntl()시스템 콜을 병행합니다.
- Windows:
2. 파일 오픈 모드별 핵심 동작과 주의사항
-
"w"/"wb"(가장 위험한 모드):- 기존 파일이 존재하면 파일 크기를 0바이트로 초기화(Truncate)한 후 새로 씁니다.
- 기존 데이터가 전부 지워지므로 영구적인 데이터 유실을 주의해야 합니다.
-
"a"/"ab"(추가/누적 모드):- 기존 내용을 보존하며, 파일 포인터의 위치와 상관없이 항상 파일 끝(EOF)에 데이터를 덧붙입니다.
- 로그 파일이나 데이터를 누적 기록할 때는 반드시
a모드를 사용해야 합니다.
-
"r+"/"rb+"(읽기/쓰기 겸용 모드):- 기존 내용은 보존되지만, 파일을 열었을 때 파일 포인터가 맨 처음(0)에 위치합니다.
- 별도로 오프셋을 이동하지 않고 쓰기를 실행하면 첫 시작 부분부터 기존 데이터를 덮어쓰기(Override)하므로 기존 데이터가 손상될 위험이 큽니다.
-
"r"/"rb"(읽기 전용 모드):- 파일이 없으면
NULL을 반환하며, 시작 오프셋 0부터 순차적으로 읽습니다.
- 파일이 없으면
3. 텍스트(Text) 모드 vs 바이너리(Binary) 모드
-
텍스트 모드 (
"r","w","a")- 사람이 읽을 수 있는 문자열 데이터를 읽고 쓸 때 사용합니다.
- 줄바꿈 자동 변환: OS 환경에 따라 개행 문자 변환이 자동으로 일어납니다. (Windows:
\n\r\n)
-
바이너리 모드 (
"rb","wb","ab")- JSON 형태의 바이트 데이터나
struct구조체 규격에 맞춰 메모리 형태 그대로(Raw Bytes) 1:1로 정해진 길이만큼 읽고 쓸 때 사용합니다. - 개행 문자 자동 변환이 일어나지 않아 바이너리 데이터가 변형되지 않습니다.
- JSON 형태의 바이트 데이터나
4. 바이너리 입출력 시 필수 검증 (Validation)
바이너리 I/O는 메모리 공간을 직접 다루므로 엄격한 유효성 검증을 거치지 않으면 버퍼 오버플로우 및 메모리 오염(Memory Corruption)이 발생합니다.
#include <stdio.h>
#include <stdint.h>
#pragma pack(push, 1) // 패딩 제거로 파일 규격 1:1 일치
typedef struct _PacketData {
uint8_t cmd;
uint32_t length;
char payload[256];
} PacketData;
#pragma pack(pop)
void process_binary_file(FILE *fp) {
PacketData packet = {0};
// 1. 읽어들인 블록 수 검증 (데이터 잘림 및 파일 손상 체크)
size_t read_count = fread(&packet, sizeof(PacketData), 1, fp);
if (read_count != 1) {
fprintf(stderr, "[오류] 파일 크기 부족 또는 바이너리 규격 불일치\n");
return;
}
// 2. 버퍼 오버플로우 방지: 페이로드 길이 검증
if (packet.length >= sizeof(packet.payload)) {
fprintf(stderr, "[오류] 비정상적인 페이로드 길이 (오버플로우 위험)\n");
return;
}
// 3. 메모리 침범 방지: 문자열 끝 NULL 종단 강제 보장
packet.payload[sizeof(packet.payload) - 1] = '\0';
}
주의 (구조체 바이너리 저장 시 포인터 사용 금지):
구조체 내부에
char *,int *등 포인터 변수가 포함되어 있을 경우,fwrite시 실 데이터가 아닌 ‘임의의 메모리 주소값’만 파일에 기록됩니다. 파일을 다시 읽었을 때 해당 주소는 허상 포인터(Dangling Pointer)가 되어 접근 시 즉시 크래시(Segmentation Fault)를 일으킵니다.